/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.annotation.support;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link EmbeddedServiceConfigurationSupport} class is an abstract base class supporting the configuration
* of Pivotal GemFire and Apache Geode embedded services.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar
* @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration
* @since 1.9.0
*/
@SuppressWarnings("unused")
public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
public static final Integer DEFAULT_PORT = 0;
public static final String DEFAULT_HOST = "localhost";
@Autowired
@SuppressWarnings("all")
private AbstractCacheConfiguration cacheConfiguration;
private BeanFactory beanFactory;
/**
* Returns a reference to an instance of the {@link AbstractCacheConfiguration} class used to configure
* a GemFire (Singleton, client or peer) cache instance along with it's associated, embedded services.
*
* @param <T> {@link Class} type extension of {@link AbstractCacheConfiguration}.
* @return a reference to a single {@link AbstractCacheConfiguration} instance.
* @throws IllegalStateException if the {@link AbstractCacheConfiguration} reference was not configured.
* @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractCacheConfiguration> T cacheConfiguration() {
Assert.state(cacheConfiguration != null, "AbstractCacheConfiguration was not properly initialized");
return (T) this.cacheConfiguration;
}
/**
* Returns the configured GemFire cache application annotation type
* (e.g. {@link org.springframework.data.gemfire.config.annotation.ClientCacheApplication}
* or {@link org.springframework.data.gemfire.config.annotation.PeerCacheApplication}.
*
* @return an {@link Class annotation} defining the GemFire cache application type.
*/
protected abstract Class getAnnotationType();
/**
* Returns the fully-qualified class name of the GemFire cache application annotation type.
*
* @return a fully-qualified class name of the GemFire cache application annotation type.
* @see java.lang.Class#getName()
* @see #getAnnotationType()
*/
protected String getAnnotationTypeName() {
return getAnnotationType().getName();
}
/**
* Returns the simple class name of the GemFire cache application annotation type.
*
* @return the simple class name of the GemFire cache application annotation type.
* @see java.lang.Class#getSimpleName()
* @see #getAnnotationType()
*/
protected String getAnnotationTypeSimpleName() {
return getAnnotationType().getSimpleName();
}
/**
* @inheritDoc
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* Returns a reference to the Spring {@link BeanFactory}.
*
* @return a reference to the Spring {@link BeanFactory}.
* @throws IllegalStateException if the Spring {@link BeanFactory} was not properly initialized.
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized");
return this.beanFactory;
}
/**
* {@inheritDoc}
*/
@Override
public final void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (isAnnotationPresent(importingClassMetadata)) {
Map<String, Object> annotationAttributes = getAnnotationAttributes(importingClassMetadata);
registerBeanDefinitions(importingClassMetadata, annotationAttributes, registry);
setGemFireProperties(importingClassMetadata, annotationAttributes, registry);
}
}
/* (non-Javadoc) */
@SuppressWarnings("unused")
protected void registerBeanDefinitions(AnnotationMetadata importingClassMetaData,
Map<String, Object> annotationAttributes, BeanDefinitionRegistry registry) {
}
/* (non-Javadoc) */
protected void setGemFireProperties(AnnotationMetadata importingClassMetadata,
Map<String, Object> annotationAttributes, BeanDefinitionRegistry registry) {
Properties gemfireProperties = toGemFireProperties(annotationAttributes);
if (hasProperties(gemfireProperties)) {
try {
cacheConfiguration().add(gemfireProperties);
}
catch (Exception ignore) {
registerGemFirePropertiesBeanPostProcessor(registry, gemfireProperties);
}
}
}
/* (non-Javadoc) */
protected abstract Properties toGemFireProperties(Map<String, Object> annotationAttributes);
/* (non-Javadoc) */
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) {
return importingClassMetadata.hasAnnotation(getAnnotationTypeName());
}
/* (non-Javadoc) */
protected boolean hasProperties(Properties properties) {
return !CollectionUtils.isEmpty(properties);
}
/* (non-Javadoc) */
protected Map<String, Object> getAnnotationAttributes(AnnotationMetadata importingClassMetadata) {
return importingClassMetadata.getAnnotationAttributes(getAnnotationTypeName());
}
/* (non-Javadoc) */
protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry,
Properties customGemFireProperties) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
GemFirePropertiesBeanPostProcessor.class);
builder.addConstructorArgValue(customGemFireProperties);
BeanDefinitionReaderUtils.registerBeanDefinition(newBeanDefinitionHolder(builder), registry);
}
/* (non-Javadoc) */
protected BeanDefinitionHolder newBeanDefinitionHolder(BeanDefinitionBuilder builder) {
return new BeanDefinitionHolder(builder.getBeanDefinition(), generateBeanName());
}
/* (non-Javadoc) */
protected String generateBeanName() {
return generateBeanName(getAnnotationTypeSimpleName());
}
/* (non-Javadoc) */
protected String generateBeanName(Class<?> typeQualifier) {
return generateBeanName(typeQualifier.getSimpleName());
}
/* (non-Javadoc) */
protected String generateBeanName(String nameQualifier) {
return String.format("%1$s.%2$s", getClass().getName(), nameQualifier);
}
/**
* Resolves a Spring managed bean with the given {@link Class} type from the Spring {@link BeanFactory}.
*
* It is assumed that the given typed bean is the only bean of this {@link Class} type. If more than 1 bean
* of the given {@link Class} type is found, then the Spring {@link BeanFactory} will throw
* a {@link org.springframework.beans.factory.NoUniqueBeanDefinitionException}.
*
* If the {@link BeanFactory} is an instance of {@link AutowireCapableBeanFactory}, then the returned bean
* will also be configured.
*
* @param <T> {@link Class} type of the registered Spring managed bean.
* @param beanType required {@link Class} type of the registered Spring managed bean.
* @return a Spring managed bean instance for the given, required {@link Class} type, or {@literal null}
* if no bean instance of the given, required {@link Class} type could be found.
* @throws BeansException if the Spring manage bean of the required {@link Class} type could not be resolved.
* @see #getBeanFactory()
*/
@SuppressWarnings("unchecked")
protected <T> T resolveBean(Class<T> beanType) {
BeanFactory beanFactory = getBeanFactory();
if (beanFactory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory autowiringBeanFactory = (AutowireCapableBeanFactory) beanFactory;
NamedBeanHolder<T> beanHolder = autowiringBeanFactory.resolveNamedBean(beanType);
return (T) autowiringBeanFactory.configureBean(beanHolder.getBeanInstance(), beanHolder.getBeanName());
}
else {
return beanFactory.getBean(beanType);
}
}
/* (non-Javadoc) */
protected String resolveHost(String hostname) {
return resolveHost(hostname, DEFAULT_HOST);
}
/* (non-Javadoc) */
protected String resolveHost(String hostname, String defaultHostname) {
return (StringUtils.hasText(hostname) ? hostname : defaultHostname);
}
/* (non-Javadoc) */
protected Integer resolvePort(Integer port) {
return resolvePort(port, DEFAULT_PORT);
}
/* (non-Javadoc) */
protected Integer resolvePort(Integer port, Integer defaultPort) {
return (port != null ? port : defaultPort);
}
/**
* Spring {@link BeanPostProcessor} used to process GemFire System properties defined as a Spring bean
* in the Spring application context before initialization.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor
*/
protected static class GemFirePropertiesBeanPostProcessor implements BeanPostProcessor {
protected static final String GEMFIRE_PROPERTIES_BEAN_NAME = "gemfireProperties";
private final Properties gemfireProperties;
/**
* Construct an instance of the {@link GemFirePropertiesBeanPostProcessor} initialized with
* the given GemFire {@link Properties}.
*
* @param gemfireProperties {@link Properties} used to configure GemFire.
* @throws IllegalArgumentException if the {@link Properties} are null or empty.
* @see java.util.Properties
*/
protected GemFirePropertiesBeanPostProcessor(Properties gemfireProperties) {
Assert.notEmpty(gemfireProperties, "GemFire Properties must not be null or empty");
this.gemfireProperties = gemfireProperties;
}
/**
* {@inheritDoc}
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Properties && GEMFIRE_PROPERTIES_BEAN_NAME.equals(beanName)) {
Properties gemfirePropertiesBean = (Properties) bean;
gemfirePropertiesBean.putAll(gemfireProperties);
}
return bean;
}
/**
* {@inheritDoc}
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
}